﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NetworkManagement;
using DataObject = LorraineSec.DatabaseLib.DataObjects;

namespace LorraineSec.MessageLib.Messages
{
    public sealed class CompartmentListResponse : EncryptedMessage
    {
        #region Format Constants
        /*
         * Format:
         *         2             4       1         x
         * | # Compartments | { ID | NameLength | Name }
         * 
         */

        private const int ENC_NUMCOMP_LENGTH = 2;
        private const int SET_ID_LENGTH = 4;
        private const int SET_NAMELEN_LENGTH = 1;    

        private const int ENC_NUMCOMPARTMENTS_OFFSET = 0;
        private const int SET_ID_OFFSET = 0;
        private const int SET_NAMELEN_OFFSET = SET_ID_OFFSET + SET_ID_LENGTH;
        private const int SET_NAME_OFFSET = SET_NAMELEN_OFFSET + SET_NAMELEN_LENGTH;

        private const int ENC_KNOWN_LENGTH = ENC_NUMCOMP_LENGTH;
        private const int SET_KNOWN_LENGTH = SET_ID_LENGTH + SET_NAMELEN_LENGTH;
        #endregion

        public struct Compartment
        {
            public Compartment(uint _id, string _name)
            {
                ID = _id;
                Name = _name;
            }
            public static implicit operator Compartment(DataObject.Compartment obj)
            {
                return new Compartment(obj.ID, obj.Name);
            }

            public uint ID;
            public string Name;
        }

        public List<Compartment> Compartments;

        public CompartmentListResponse()
        {
            init();
        }

        public CompartmentListResponse(byte[] bytes)
            : base(bytes)
        {
            init();
            decode();
        }

        private void init()
        {
            MessageID = (ushort)LorraineSecMessageFactory.MessageID.COMPARTMENT_LIST_RESPONSE;
            Compartments = new List<Compartment>();
        }

        public override void decrypt(CryptoManager crypt)
        {
            base.decrypt(crypt);
            //Decode byte array
            //Get number of compartments
            ushort numComp = Bytes.getUInt16(encData, ENC_NUMCOMPARTMENTS_OFFSET);
            //Set offset variable
            int offset = ENC_NUMCOMPARTMENTS_OFFSET + ENC_NUMCOMP_LENGTH;
            for (int i = 0; i < numComp; ++i)
            {
                //Get compartment ID
                uint id = Bytes.getUInt32(encData, offset);
                offset += SET_ID_LENGTH;
                //Get length of compartment name
                int nameLen = Bytes.getByte(encData, offset);
                offset += SET_NAMELEN_LENGTH;
                //Get compartment name
                string name = Bytes.getString(encData, offset, nameLen);
                offset += nameLen;
                //Add compartment to dictionary
                Compartments.Add(new Compartment(id,name));
            }
        }

        public override void encrypt(CryptoManager crypt)
        {
            //Encode byte array
            //Precount byte array allocation size
            int strAllocSize = 0;
            foreach (Compartment cmp in Compartments)
            {
                strAllocSize += cmp.Name.Length;
            }
            //Allocate memory for encrypion field
            encData = new byte[ENC_KNOWN_LENGTH + Compartments.Count * SET_KNOWN_LENGTH + strAllocSize];

            //Set the number of compartments
            Bytes.setValue(encData, ENC_NUMCOMPARTMENTS_OFFSET, (ushort)Compartments.Count);
            //Set offset variable
            int offset = ENC_NUMCOMPARTMENTS_OFFSET + ENC_NUMCOMP_LENGTH;
            foreach (Compartment comp in Compartments)
            {
                //Set compartment ID
                Bytes.setValue(encData, offset, comp.ID);
                offset += SET_ID_LENGTH;
                //Set compartment name length
                Bytes.setValue(encData, offset, (byte)comp.Name.Length);
                offset += SET_NAMELEN_LENGTH;
                //Set compartment name
                Bytes.setValue(encData, offset, comp.Name);
                offset += comp.Name.Length;
            }

            base.encrypt(crypt);
        }
    }
}
